/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package dk.rofl.maps;

import dk.rofl.graphics.GameTileSet;
import dk.rofl.units.GameUnit;
import dk.rofl.units.Monster;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import rofl.Utils;

/**
 *
 * @author menderleit
 */
public class GameMap {

    private int mapWidth;
    private int mapHeight;
    private Point startPos;
    private Point endPos;
    private ArrayList<Point> pointOfInterest;
    private MapCell[][] cells;
    private GameTileSet gts;

    public GameMap(int width, int height, GameTileSet gts) {
        mapWidth = width;
        mapHeight = height;
        startPos = new Point(1, 1);
        endPos = new Point(mapWidth - 2, mapHeight - 2);
        cells = new MapCell[mapWidth][mapHeight];
        for (int y = 0; y < mapHeight; y++) {
            for (int x = 0; x < mapWidth; x++) {
                cells[x][y] = new MapCell();
            }
        }
        this.gts = gts;
    }

    // Build a list of PointsOfInterest's.
    public void buildPOIList() {
        pointOfInterest = new ArrayList();
        pointOfInterest.add(findPointNextTo(endPos));
        for (int y = 0; y < mapHeight; y++) {
            for (int x = 0; x < mapWidth; x++) {
                if (isEmpty(x, y)) {
                    if (isPointOfInterest(x, y)) {
                        if (!isPointNextTo(new Point(x, y), startPos) && !isPointNextTo(new Point(x, y), endPos)) {
                            pointOfInterest.add(new Point(x, y));
                        }
                    }
                }
            }
        }
    }

    // Check if this qualifies as a POI.
    public boolean isPointOfInterest(int x, int y) {
        boolean poi = false;
        int count = 0;
        if (isEmpty(x - 1, y)) {
            count++;
        }
        if (isEmpty(x + 1, y)) {
            count++;
        }
        if (isEmpty(x, y - 1)) {
            count++;
        }
        if (isEmpty(x, y + 1)) {
            count++;
        }
        if (count == 1) {
            poi = true;
        }
        return poi;
    }

    public ArrayList<Point> getPointOffInterest() {
        return pointOfInterest;
    }

    public void setPointOffInterest(ArrayList<Point> pointOffInterest) {
        this.pointOfInterest = pointOffInterest;
    }

    public void insertMonster(Point p, Monster m) {
        insertMonster(p.x, p.y, m);
    }

    public void insertMonster(int x, int y, Monster m) {
        if (x >= 0 && x < mapWidth && y >= 0 && y < mapHeight) {
            cells[x][y].placeMonster(m);
        }
    }

    public void removeMonster(Point p) {
        removeMonster(p.x, p.y);
    }

    public void removeMonster(int x, int y) {
        if (x >= 0 && x < mapWidth && y >= 0 && y < mapHeight) {
            cells[x][y].removeMonster();
        }
    }

    public Monster getMonster(int x, int y) {
        return cells[x][y].getMonster();
    }

    public void setStartPosition(Point p) {
        if (p.x >= 0 && p.x < mapWidth && p.y >= 0 && p.y < mapHeight) {
            startPos = p;
        }
    }

    public void setStartPosition(int x, int y) {
        if (x >= 0 && x < mapWidth && y >= 0 && y < mapHeight) {
            startPos = new Point(x, y);
        }
    }

    public Point getStartPosition() {
        return startPos;
    }

    public void setEndPosition(Point p) {
        if (p.x >= 0 && p.x < mapWidth && p.y >= 0 && p.y < mapHeight) {
            endPos = p;
        }
    }

    public void setEndPosition(int x, int y) {
        if (x >= 0 && x < mapWidth && y >= 0 && y < mapHeight) {
            endPos = new Point(x, y);
        }
    }

    public Point getEndPosition() {
        return endPos;
    }

    public void fillVisionMap(boolean vis) {
        for (int y = 0; y < mapHeight; y++) {
            for (int x = 0; x < mapWidth; x++) {
                cells[x][y].setVisible(vis);
            }
        }
    }

    public void setVisible(Point p) {
        for (int vy = p.y - 2; vy < p.y + 3; vy++) {
            for (int vx = p.x - 2; vx < p.x + 3; vx++) {
                if (vx >= 0 && vx < mapWidth && vy >= 0 && vy < mapHeight) {
                    if (vy == p.y - 2 || vy == p.y + 2) {
                        if (vx > p.x - 2 && vx < p.x + 2) {
                            cells[vx][vy].setVisible(true);
                        }
                    } else {
                        cells[vx][vy].setVisible(true);
                    }
                }
            }
        }
    }

    public void setVisible(int x, int y) {
        for (int vy = y - 2; vy < y + 3; vy++) {
            for (int vx = x - 2; vx < x + 3; vx++) {
                if (vx >= 0 && vx < mapWidth && vy >= 0 && vy < mapHeight) {
                    if (vy == y - 2 || vy == y + 2) {
                        if (vx > x - 2 && vx < x + 2) {
                            cells[vx][vy].setVisible(true);
                        }
                    } else {
                        cells[vx][vy].setVisible(true);
                    }
                }
            }
        }
    }

    public boolean getVisible(int x, int y) {
        boolean vis = false;
        if (x >= 0 && x < mapWidth && y >= 0 && y < mapHeight) {
            vis = cells[x][y].getVisible();
        }
        return vis;
    }

    public void fillCollisionMap(boolean col) {
        for (int y = 0; y < mapHeight; y++) {
            for (int x = 0; x < mapWidth; x++) {
                cells[x][y].setCollision(col);
            }
        }
    }

    public void setCollision(int x, int y, boolean col) {
        if (x >= 0 && x < mapWidth && y >= 0 && y < mapHeight) {
            cells[x][y].setCollision(col);
        }
    }

    public boolean getCollision(int x, int y) {
        boolean col = true;
        if (x >= 0 && x < mapWidth && y >= 0 && y < mapHeight) {
            col = cells[x][y].getCollision();
        }
        return col;
    }

    public int getWidth() {
        return mapWidth;
    }

    public int getHeight() {
        return mapHeight;
    }

    public int getTileSize() {
        return gts.getTileSize();
    }

    // Puts the right tile images in the right places. :)
    public void buildTileMap() {
        cells[0][0].setTileNum(5);
        cells[mapWidth - 1][0].setTileNum(6);
        cells[mapWidth - 1][mapHeight - 1].setTileNum(7);
        cells[0][mapHeight - 1].setTileNum(8);
        for (int y = 0; y < mapHeight; y++) {
            for (int x = 0; x < mapWidth; x++) {
                if (cells[x][y].getCollision()) {
                    if (y == 0 && x > 0 && x < mapWidth - 1) {
                        if (!cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(14);
                        } else {
                            cells[x][y].setTileNum(10);
                        }
                    }
                    if (y == mapHeight - 1 && x > 0 && x < mapWidth - 1) {
                        if (!cells[x][y - 1].getCollision()) {
                            cells[x][y].setTileNum(14);
                        } else {
                            cells[x][y].setTileNum(12);
                        }
                    }
                    if (x == 0 && y > 0 && y < mapHeight - 1) {
                        if (!cells[x + 1][y].getCollision()) {
                            cells[x][y].setTileNum(15);
                        } else {
                            cells[x][y].setTileNum(9);
                        }
                    }
                    if (x == mapWidth - 1 && y > 0 && y < mapHeight - 1) {
                        if (!cells[x - 1][y].getCollision()) {
                            cells[x][y].setTileNum(15);
                        } else {
                            cells[x][y].setTileNum(11);
                        }
                    }
                    if (x > 0 && x < mapWidth - 1 && y > 0 && y < mapHeight - 1) {
                        if (!cells[x - 1][y].getCollision() && cells[x + 1][y].getCollision() && !cells[x][y - 1].getCollision() && !cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(1);
                        }
                        if (!cells[x - 1][y].getCollision() && !cells[x + 1][y].getCollision() && !cells[x][y - 1].getCollision() && cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(2);
                        }
                        if (cells[x - 1][y].getCollision() && !cells[x + 1][y].getCollision() && !cells[x][y - 1].getCollision() && !cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(3);
                        }
                        if (!cells[x - 1][y].getCollision() && !cells[x + 1][y].getCollision() && cells[x][y - 1].getCollision() && !cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(4);
                        }
                        if (!cells[x - 1][y].getCollision() && cells[x + 1][y].getCollision() && !cells[x][y - 1].getCollision() && cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(5);
                        }
                        if (cells[x - 1][y].getCollision() && !cells[x + 1][y].getCollision() && !cells[x][y - 1].getCollision() && cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(6);
                        }
                        if (cells[x - 1][y].getCollision() && !cells[x + 1][y].getCollision() && cells[x][y - 1].getCollision() && !cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(7);
                        }
                        if (!cells[x - 1][y].getCollision() && cells[x + 1][y].getCollision() && cells[x][y - 1].getCollision() && !cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(8);
                        }
                        if (!cells[x - 1][y].getCollision() && cells[x + 1][y].getCollision() && cells[x][y - 1].getCollision() && cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(9);
                        }
                        if (cells[x - 1][y].getCollision() && cells[x + 1][y].getCollision() && !cells[x][y - 1].getCollision() && cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(10);
                        }
                        if (cells[x - 1][y].getCollision() && !cells[x + 1][y].getCollision() && cells[x][y - 1].getCollision() && cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(11);
                        }
                        if (cells[x - 1][y].getCollision() && cells[x + 1][y].getCollision() && cells[x][y - 1].getCollision() && !cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(12);
                        }
                        if (cells[x - 1][y].getCollision() && cells[x + 1][y].getCollision() && cells[x][y - 1].getCollision() && cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(13);
                        }
                        if (cells[x - 1][y].getCollision() && cells[x + 1][y].getCollision() && !cells[x][y - 1].getCollision() && !cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(14);
                        }
                        if (!cells[x - 1][y].getCollision() && !cells[x + 1][y].getCollision() && cells[x][y - 1].getCollision() && cells[x][y + 1].getCollision()) {
                            cells[x][y].setTileNum(15);
                        }
                    }
                } else {
                    cells[x][y].setTileNum(0);
                }
            }
        }
        cells[startPos.x][startPos.y].setTileNum(17);
        cells[endPos.x][endPos.y].setTileNum(16);
    }

    // Find a completely empty position. (Does not include floors)
    public Point findEmpty() {
        Point p = null;
        boolean found = false;
        while (!found) {
            int x = Utils.rnd().nextInt(mapWidth);
            int y = Utils.rnd().nextInt(mapHeight);
            if (isEmpty(x, y)) {
                p = new Point(x, y);
                found = true;
            }
        }
        return p;
    }
    // Find a empty point of interrest that i not the endpoint.

    public Point findEmptyPointOfInterest() {
        ArrayList<Point> al = new ArrayList();
        for (int i = 0; i < pointOfInterest.size(); i++) {
            if (isEmpty(pointOfInterest.get(i))) {
                al.add(pointOfInterest.get(i));
            }
        }
        int r = Utils.rnd().nextInt(al.size());
        return al.get(r);
    }

    public boolean isEmpty(Point p) {
        return isEmpty(p.x, p.y);
    }

    // Check if a position is completely empty. (Does not include floors)
    public boolean isEmpty(int x, int y) {
        boolean empty = false;
        if (x >= 0 && x < mapWidth && y >= 0 && y < mapHeight) {
            if (!getCollision(x, y)) {
                if (!((x == startPos.x) && (y == startPos.y)) && !((x == endPos.x) && (y == endPos.y))) {
                    if (getMonster(x, y) == null) {
                        empty = true;
                    }
                }
            }
        }
        return empty;
    }

    public boolean isPointNextTo(Point a, Point b) {
        boolean nextTo = false;

        if (a.x == b.x - 1 && a.y == b.y) {
            nextTo = true;
        }
        if (a.x == b.x + 1 && a.y == b.y) {
            nextTo = true;
        }
        if (a.x == b.x && a.y == b.y - 1) {
            nextTo = true;
        }
        if (a.x == b.x && a.y == b.y + 1) {
            nextTo = true;
        }

        return nextTo;
    }

    public Point findPointNextTo(Point point) {
        Point p = null;
        int x = point.x;
        int y = point.y;

        if (isEmpty(x, y - 1)) {
            p = new Point(x, y - 1);
        }

        if (isEmpty(x, y + 1)) {
            p = new Point(x, y + 1);
        }

        if (isEmpty(x - 1, y)) {
            p = new Point(x - 1, y);
        }

        if (isEmpty(x + 1, y)) {
            p = new Point(x + 1, y);
        }

        return p;
    }

    // Add monsters to the map.
    public void populate(int gameLevel) {
        int r;

        if ((gameLevel % 5) == 0) {
            Monster boss = Monster.generateMonster(gameLevel, Monster.TYPE_BOSS);
            insertMonster(pointOfInterest.get(0), boss);
        }

        if (gameLevel > 1) {
            if (gameLevel > 2) {
                if (gameLevel > 3) {
                    // Create champion monsters.
                    r = Utils.rnd().nextInt(2) + 2;
                    for (int i = 0; i < r; i++) {
                        Point p = findPointNextTo(findEmptyPointOfInterest());
                        if (p != null) {
                            Monster m = Monster.generateMonster(gameLevel, Monster.TYPE_ELITE);
                            insertMonster(p.x, p.y, m);
                        }
                    }
                }
                // Create elite monsters.
                r = Utils.rnd().nextInt(3) + 3;
                for (int i = 0; i < r; i++) {
                    Point p = findPointNextTo(findEmptyPointOfInterest());
                    if (p != null) {
                        Monster m = Monster.generateMonster(gameLevel, Monster.TYPE_STRONG);
                        insertMonster(p.x, p.y, m);
                    }
                }
            }
            // Create strong monsters.
            r = Utils.rnd().nextInt(5) + 5;
            for (int i = 0; i < r; i++) {
                Point p = findEmpty();
                if (p != null) {
                    Monster m = Monster.generateMonster(gameLevel, Monster.TYPE_NORMAL);
                    insertMonster(p.x, p.y, m);
                }
            }
        }

        // Create normal monsters.
        r = Utils.rnd().nextInt(10) + 10;
        for (int i = 0; i < r; i++) {
            Point p = findEmpty();
            Monster m = Monster.generateMonster(gameLevel, Monster.TYPE_WEAK);
            insertMonster(p.x, p.y, m);
        }
    }

    public void draw(Graphics2D g, int xPos, int yPos) {
        int x, y;
        int scale = gts.getTileSize();
        for (y = 0; y < mapHeight; y++) {
            for (x = 0; x < mapWidth; x++) {
                if (cells[x][y].getVisible()) {
                    g.drawImage(gts.getTileImage(cells[x][y].getTileNum()), null, x * scale + xPos, y * scale + yPos);
                    if (cells[x][y].getMonster() != null) {
                        Monster m = cells[x][y].getMonster();
                        g.drawImage(m.getImage(), null, x * scale + xPos, y * scale + yPos);
                        if (!m.isDead()) {
                            int type = m.getMobType();
                            String s = "";
                            switch (type) {
                                case Monster.TYPE_WEAK:
                                    s = "W";
                                    break;
                                case Monster.TYPE_STRONG:
                                    s = "S";
                                    break;
                                case Monster.TYPE_ELITE:
                                    s = "E";
                                    break;
                                case Monster.TYPE_BOSS:
                                    s = "B";
                                    break;
                            }
                            g.setColor(Color.black);
                            g.drawString(s, x * scale + xPos + 1, y * scale + yPos + 31);
                            g.setColor(Color.yellow);
                            g.drawString(s, x * scale + xPos, y * scale + yPos + 30);
                        }
                    }
                }
            }
        }
//        for (int i = 0; i < pointOfInterest.size(); i++) {
//            g.setColor(Color.yellow);
//            g.fillOval(pointOfInterest.get(i).x * scale + xPos + scale / 4, pointOfInterest.get(i).y * scale + yPos + scale / 4, scale / 2, scale / 2);
//        }
    }
}
